/*
 * $Id: PerforceDecorationRunnable.java 23 2002-09-29 13:59:30Z bpruessmann $
 * 
 * Copyright (c) 2001,2002 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 */
package net.sourceforge.p4eclipse.ui;

import java.util.ArrayList;
import java.util.List;

import net.sourceforge.p4eclipse.core.PerforceRepositoryProvider;
import net.sourceforge.p4eclipse.core.resources.LocalFile;
import net.sourceforge.p4eclipse.core.resources.LocalResource;
import net.sourceforge.p4eclipse.core.syncinfo.ResourceSyncInfo;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.team.core.RepositoryProvider;

/**
  * @version 	$Revision: 23 $
  * @author		<a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  */
class PerforceDecorationRunnable implements Runnable
{
	private final static int NUM_TO_BATCH = 50;
	
	private IDecorationNotifier notifier;

	private ImageDescriptor outOfDate   = new CachedImageDescriptor(PerforceUIPlugin.getPlugin().getImageDescriptor(PerforceUIPlugin.OVR_OUTOFDATE));
	private ImageDescriptor hasRemote   = new CachedImageDescriptor(PerforceUIPlugin.getPlugin().getImageDescriptor(PerforceUIPlugin.OVR_HASREMOTE));
	private ImageDescriptor openForAdd  = new CachedImageDescriptor(PerforceUIPlugin.getPlugin().getImageDescriptor(PerforceUIPlugin.OVR_OPENFORADD));
	private ImageDescriptor openForEdit = new CachedImageDescriptor(PerforceUIPlugin.getPlugin().getImageDescriptor(PerforceUIPlugin.OVR_OPENFOREDIT));	

	/*
	 * Define a cached image descriptor which only creates the image data once
	 */
	public class CachedImageDescriptor extends ImageDescriptor 
	{
		ImageDescriptor descriptor;
		ImageData data;
		
		public CachedImageDescriptor(ImageDescriptor descriptor) 
		{
			this.descriptor = descriptor;
		}
		
		public ImageData getImageData() 
		{
			if (data == null) 
				data = descriptor.getImageData();

			return data;
		}
	}
	
	public PerforceDecorationRunnable(IDecorationNotifier notifier)
	{
		this.notifier = notifier;
	}

	/*
	 * @see Runnable#run()
	 */
	public void run()
	{
		List resources = new ArrayList();
		List decorations = new ArrayList();
		
		while (true)
		{
			IResource resource = notifier.next();
			if (resource == null)
				return;
				
			PerforceDecoration decoration = decorate(resource);
			if (decoration != null)
			{
				resources.add(resource);
				decorations.add(decoration);
				
				if(!resources.isEmpty() && (notifier.remaining()==0 || resources.size() >= NUM_TO_BATCH)) 
				{
					notifier.decorated((IResource[])resources.toArray(new IResource[0]), 
									   (PerforceDecoration[])decorations.toArray(new PerforceDecoration[0]));
					resources.clear();
					decorations.clear();					
				}
			}
		}
	}

	private PerforceDecoration decorate(IResource resource) 
	{
		RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject());
		if (!resource.exists() || provider == null || !provider.getID().equals(PerforceUIPlugin.getTypeId()))
			return null;
		
		IPreferenceStore store = PerforceUIPlugin.getPlugin().getPreferenceStore();
		boolean isDirty = false;
		boolean computeDeepDirtyCheck = store.getBoolean(PerforceUIPlugin.CALCULATE_DIRTY_PREFERENCE);
		if(resource.getType() == IResource.FILE || computeDeepDirtyCheck) 
			isDirty = isDirty(resource);							
		
		String text = computeDecoratedText((PerforceRepositoryProvider)provider, isDirty, resource);
		List overlays = computeOverlays((PerforceRepositoryProvider)provider, isDirty, resource);
		
		return new PerforceDecoration(text, overlays);
	}

	private String computeDecoratedText(PerforceRepositoryProvider provider, boolean isDirty, IResource resource)
	{
		String result = "{0}";
		
		switch (resource.getType())
		{
			case IResource.PROJECT:
				{
					result += " [" + PerforceUIPlugin.getClient().getPerforcePort() + "]"; 
				}
				
			case IResource.FOLDER:
				{
					if (isDirty)
						result = ">" + result; //$NON-NLS-1$				
				}
				break;
				
			case IResource.FILE:
				{
					LocalFile localFile = (LocalFile)LocalResource.createFromResource(resource);
					
					ResourceSyncInfo resourceSyncInfo = localFile.getSyncInfo();
					if (!resourceSyncInfo.isNull())
					{
						if (resourceSyncInfo.getAction().equals(ResourceSyncInfo.ACTION_ADD))
							result = "*" + result;
						else
						if (resourceSyncInfo.getAction().equals(ResourceSyncInfo.ACTION_EDIT))
						{
							result = ">" + result; //$NON-NLS-1$										
							result += " #" + resourceSyncInfo.getHaveRev(); //$NON-NLS-1$
						}
						else
						{
							result += " #" + resourceSyncInfo.getHaveRev(); //$NON-NLS-1$
						}
					}
				}
				break;		
		}
		return result;
	}
	
	private List computeOverlays(PerforceRepositoryProvider provider, boolean isDirty, IResource resource)
	{
		List result = new ArrayList();		
		switch (resource.getType())
		{
			case IResource.PROJECT:
			case IResource.FOLDER:	
				LocalResource localResource = LocalResource.createFromResource(resource);
				{
					if (localResource.hasRemote())
						result.add(hasRemote);
				}		
				break;
				
			case IResource.FILE:
				{
					LocalFile localFile = (LocalFile)LocalResource.createFromResource(resource);
					
					if (localFile.hasRemote())
					{
						if (localFile.isOutOfDate())
							result.add(outOfDate);
						else
							result.add(hasRemote);
					}
						
					ResourceSyncInfo resourceSyncInfo = localFile.getSyncInfo();
					if (!resourceSyncInfo.isNull())
					{
						if (resourceSyncInfo.getAction().equals(ResourceSyncInfo.ACTION_ADD))
							result.add(openForAdd);
						else
						if (resourceSyncInfo.getAction().equals(ResourceSyncInfo.ACTION_EDIT))
							result.add(openForEdit);
					}
				}
				break;
		}
		
		return result;
	}

	private boolean isDirty(IResource resource)
	{
		final CoreException DECORATOR_EXCEPTION = new CoreException(new Status(IStatus.OK, "id", 1, "", null)); //$NON-NLS-1$ //$NON-NLS-2$
		try
		{
			resource.accept(new IResourceVisitor()
			{
				public boolean visit(IResource resource) throws CoreException
				{

					// a project can't be dirty, continue with its children
					if (resource.getType() == IResource.PROJECT)
						return true;

					// if the resource does not exist in the workbench or on the file system, stop searching.
					if (!resource.exists())
						return false;
						
					LocalResource localResource = LocalResource.createFromResource(resource);
					if (localResource.isIgnored())
						return false;

					if (localResource instanceof LocalFile)
					{
						if (localResource.isDirty())
							throw DECORATOR_EXCEPTION;
					}

					return true;
				}
			}, IResource.DEPTH_INFINITE, true);
		}
		catch (CoreException e)
		{
			//if our exception was caught, we know there's a dirty child
			return e == DECORATOR_EXCEPTION;
		}
		
		return false;
	}
}
